概述
大语言模型(LLM)生成结构化输出的能力,对于依赖可靠解析输出值的下游应用至关重要。开发者希望能够快速将 AI 模型的输出结果转换为 JSON、XML 或 Java 类等数据类型,以便传递给其他应用函数和方法使用。
Spring AI 的结构化输出转换器(Structured Output Converters) 帮助将 LLM 输出转换为结构化格式。如下图所示,这种方式围绕 LLM 文本补全端点(text completion endpoint)运作:

使用通用补全 API 从大语言模型(LLM)生成结构化输出,需要对输入和输出进行谨慎处理。结构化输出转换器在 LLM 调用前后都扮演着关键角色,确保最终获得期望的输出结构。
转换器的工作流程如下:
- 在 LLM 调用之前,转换器会向提示词(Prompt)追加格式指令,为模型生成期望的输出结构提供明确指导。这些指令如同蓝图一般,引导模型的响应符合指定格式。
- 在 LLM 调用之后,转换器接收模型的输出文本,并将其转换为结构化类型的实例。这一转换过程涉及解析原始文本输出,并将其映射到对应的结构化数据表示形式,例如 JSON、XML 或特定领域的数据结构。
StructuredOutputConverter 接口
StructuredOutputConverter 接口允许你获取结构化输出,例如将基于文本的 AI 模型输出映射为 Java 类或一组值。接口定义如下:
1 | public interface StructuredOutputConverter<T> extends Converter<String, T>, FormatProvider { |
下图展示了使用结构化输出 API 时的数据流:

FormatProvider 向 AI 模型提供特定的格式指南,使其生成能够被 Converter 转换为指定目标类型 T 的文本输出。格式指令通常通过使用 PromptTemplate 追加到用户输入的末尾,示例如下:
- FormatProvider:提供格式指令,告诉模型应该如何组织输出。
- Converter:负责将模型的输出文本转换为指定类型
T的实例。
1 | String userInput = "Give me a list of actors and their filmographies"; |
内置转换器实现
Spring AI 目前提供了以下几种转换器实现:
| 转换器 | 说明 |
|---|---|
AbstractConversionServiceOutputConverter |
提供预配置的 GenericConversionService,用于将 LLM 输出转换为期望的格式。不提供默认的 FormatProvider 实现。 |
AbstractMessageOutputConverter |
提供预配置的 MessageConverter,用于将 LLM 输出转换为期望的格式。不提供默认的 FormatProvider 实现。 |
BeanOutputConverter |
配置指定的 Java 类(如 Bean)或 ParameterizedTypeReference,使用 FormatProvider 实现指导 AI 模型生成符合 DRAFT_2020_12 JSON Schema 的 JSON 响应,然后利用 JsonMapper 将 JSON 输出反序列化为目标类的 Java 对象实例。 |
MapOutputConverter |
扩展 AbstractMessageOutputConverter,包含引导 AI 模型生成符合 RFC8259 标准的 JSON 响应的 FormatProvider 实现,并利用 MessageConverter 将 JSON 负载转换为 java.util.Map 实例。 |
ListOutputConverter |
扩展 AbstractConversionServiceOutputConverter,包含针对逗号分隔列表输出的 FormatProvider 实现,利用 ConversionService 将模型文本输出转换为 java.util.List。 |
内置转换使用示例
BeanOutputConverter — 演员电影作品
以下示例展示如何使用 BeanOutputConverter 生成演员的电影作品列表。
目标记录类(Record):
1 | public record ActorsFilms(String actor, List<String> films) {} |
使用高级流式 ChatClient API:
1 | ChatClient chatClient = ChatClient.builder(chatModel).build(); |
或直接使用底层 ChatModel API:
1 | BeanOutputConverter<ActorsFilms> converter = new BeanOutputConverter<>(ActorsFilms.class); |
自定义属性排序
BeanOutputConverter 支持通过 @JsonPropertyOrder 注解在生成的 JSON Schema 中自定义属性排序。该注解允许你指定属性在 Schema 中出现的确切顺序,不受类中属性声明顺序的影响:
1 | ({"actor", "films"}) |
该注解同时适用于 Record 和常规 Java 类。
使用 ParameterizedTypeReference
使用 ParameterizedTypeReference 构造函数可以指定更复杂的目标类结构。例如,表示多个演员及其电影作品列表:
1 | ParameterizedTypeReference<List<ActorsFilms>> typeRef = |
或直接使用底层 ChatModel API:
1 | BeanOutputConverter<List<ActorsFilms>> converter = |
MapOutputConverter — 转换为 Map
以下示例展示如何使用 MapOutputConverter 将模型输出转换为包含数字列表的 Map:
1 | MapOutputConverter converter = new MapOutputConverter(); |
或直接使用底层 ChatModel API:
1 | MapOutputConverter converter = new MapOutputConverter(); |
ListOutputConverter — 转换为 List
以下示例展示如何使用 ListOutputConverter 将模型输出转换为冰淇淋口味列表:
1 | ListOutputConverter converter = new ListOutputConverter(new DefaultConversionService()); |
或直接使用底层 ChatModel API:
1 | ListOutputConverter converter = new ListOutputConverter(new DefaultConversionService()); |
原生结构化输出(Native Structured Output)
许多现代 AI 模型现已提供对结构化输出的原生支持,与基于提示词的格式控制相比,这种方式能提供更可靠的结果。Spring AI 通过原生结构化输出(Native Structured Output) 特性支持这一能力。
使用原生结构化输出时,由 BeanOutputConverter 生成的 JSON Schema 会直接发送给模型的结构化输出 API,无需在提示词中追加格式指令。这种方式具有以下优势:
| 优势 | 说明 |
|---|---|
| 更高的可靠性 | 模型保证输出符合 Schema 约束 |
| 更简洁的提示词 | 无需追加格式指令 |
| 更好的性能 | 模型可以在内部针对结构化输出进行优化 |
启用原生结构化输出
要启用原生结构化输出,使用 AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT 参数:
1 | ChatClient chatClient = ChatClient.builder(chatModel) |
也可以在使用 ChatClient.Builder 的 defaultAdvisors() 时全局启用:
1 | ChatClient chatClient = ChatClient.builder(chatModel) |
支持原生结构化输出的模型
| 模型提供商 | 支持情况 |
|---|---|
| OpenAI | GPT-4o 及更高版本,支持 JSON Schema |
| Anthropic | Claude 3.5 Sonnet 及更高版本 |
| Google GenAI | Gemini 1.5 Pro 及更高版本 |
| Mistral AI | Mistral Small 及更高版本,支持 JSON Schema |
Ollama 模型的注意事项
并非所有 Ollama 模型都能可靠地遵守结构化输出 Schema 约束。特别是内置了推理或”思考”模式的模型(例如 qwen3:8b、qwen3.5:9b 及其他较新的 Qwen 变体),可能会将内部推理过程以纯文本形式返回,而非结构化 JSON,从而导致 BeanOutputConverter 反序列化错误,例如:
1 | com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type ... |
如果在 Ollama 中遇到此问题,可以尝试换用其他模型(例如 llama3.1:latest),或回退到默认的基于提示词的方式。你也可以结合使用 useProviderStructuredOutput() 和 validateSchema(),使格式错误的响应能够自动重试。
OpenAI 的限制
OpenAI 结构化输出 API 不接受顶层 JSON 数组作为响应 Schema(详见 OpenAI 社区讨论)。在启用原生结构化输出时请求 List 将导致 API 错误。请改用以下替代方案之一:
- 使用包装类(Wrapper Class)代替
List - 使用
ParameterizedTypeReference配合自定义结构
各模型的结构化输出配置选项
一些 AI 模型提供了专用配置选项来生成结构化(通常为 JSON)输出:
OpenAI
OpenAI 的结构化输出(Structured Outputs) 可以确保模型生成严格符合你提供的 JSON Schema 的响应。你可以选择:
JSON_OBJECT:保证模型生成的消息是有效的 JSONJSON_SCHEMA:配合提供的 Schema,保证模型生成匹配你提供的 Schema 的响应
配置选项:spring.ai.openai.chat.response-format
Ollama
Ollama 提供 spring.ai.ollama.chat.format 选项来指定响应返回的格式。目前唯一接受的值为 json。
Mistral AI
Mistral AI 提供 spring.ai.mistralai.chat.response-format 选项来指定响应返回的格式。设置为 json 可启用 JSON 模式,保证模型生成的消息是有效的 JSON。此外,设置为 { "type": "json_schema" } 并配合提供的 Schema,可启用原生结构化输出支持,保证模型生成匹配你提供的 Schema 的响应。